<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="r6VNy" id="r6VNy"><span data-lake-id="u9fec15b3" id="u9fec15b3">典型回答</span></h1>
  <p data-lake-id="u9d41242f" id="u9d41242f"><br></p>
  <p data-lake-id="ua301dadb" id="ua301dadb"><span data-lake-id="ud289a500" id="ud289a500">在JDK 1.6及之前的版本中，synchronized锁是通过对象内部的一个叫做监视器锁（也称对象锁）来实现的。当一个线程请求对象锁时，如果该对象没有被锁住，线程就会获取锁并继续执行。如果该对象已经被锁住，线程就会进入阻塞状态，直到锁被释放。这种锁的实现方式称为</span><strong><span data-lake-id="u58ae11aa" id="u58ae11aa">“重量级锁”</span></strong><span data-lake-id="u24d31592" id="u24d31592">，因为获取锁和释放锁都需要在操作系统层面上进行线程的阻塞和唤醒，而这些操作会带来很大的开销。</span></p>
  <p data-lake-id="ufdf72363" id="ufdf72363"><span data-lake-id="uc3071a3c" id="uc3071a3c">​</span><br></p>
  <p data-lake-id="u8d7b15b5" id="u8d7b15b5"><span data-lake-id="u4edaf7c9" id="u4edaf7c9">在JDK 1.6之后，synchronized锁的实现发生了一些变化，引入了“偏向锁”、“轻量级锁”和“重量级锁”三种不同的状态，用来适应不同场景下的锁竞争情况。</span></p>
  <p data-lake-id="uc4f1b8e3" id="uc4f1b8e3"><span data-lake-id="u12603e39" id="u12603e39">​</span><br></p>
  <p data-lake-id="ub2b3f2ab" id="ub2b3f2ab"><u><span data-lake-id="ucc7933b9" id="ucc7933b9">在JDK 15 中，废弃了偏向锁（</span></u><a href="https://openjdk.org/jeps/374" target="_blank" data-lake-id="u0259b438" id="u0259b438"><u><span data-lake-id="uabe33153" id="uabe33153">https://openjdk.org/jeps/374</span></u></a><u><span data-lake-id="uabea8eee" id="uabea8eee"> ）</span></u></p>
  <p data-lake-id="u636c0423" id="u636c0423"><span data-lake-id="u6be00b03" id="u6be00b03">​</span><br></p>
  <p data-lake-id="ucaf9ca24" id="ucaf9ca24"><span data-lake-id="uf0c237be" id="uf0c237be">所以，在Java中，锁的状态分为四种，分别是无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态。在Java中，mark word的低两位用于表示锁的状态，分别为“01”（无锁状态）、“01”（偏向锁状态）、“00”（轻量级锁状态）和“10”（重量级锁状态）。但是由于无锁状态和偏向锁都是"01"，所以在低三位引入偏向锁标记位，用"0"表示无锁，"1"表示偏向。</span></p>
  <p data-lake-id="ue867fce8" id="ue867fce8"><span data-lake-id="u27bdf484" id="u27bdf484">​</span><br></p>
  <p data-lake-id="u062424a9" id="u062424a9"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1683549141839-20956e27-844b-4594-81f5-fc7bc3647829.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_57%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u7b3e1811" id="u7b3e1811"><span data-lake-id="uf3ac30fb" id="uf3ac30fb">​</span><br></p>
  <p data-lake-id="ud95ed71d" id="ud95ed71d"><span data-lake-id="u70b9fd27" id="u70b9fd27">以下偏向过程基于OpenJDK 8 源码总结的，源码地址：</span><a href="https://github.com/openjdk/jdk8u/blob/master/hotspot/src/share/vm/runtime/synchronizer.cpp" target="_blank" data-lake-id="ub1ea5b6a" id="ub1ea5b6a"><span data-lake-id="u68341c00" id="u68341c00">https://github.com/openjdk/jdk8u/blob/master/hotspot/src/share/vm/runtime/synchronizer.cpp</span></a></p>
  <p data-lake-id="u74b4d9f0" id="u74b4d9f0"><span data-lake-id="u0e3b41c2" id="u0e3b41c2">​</span><br></p>
  <p data-lake-id="uc928af9b" id="uc928af9b"><span data-lake-id="u70620ac1" id="u70620ac1">具体的锁升级过程如下，这里为了方便大家理解，暂时不包含自旋相关的内容，锁升级过程中的自旋参考：</span></p>
  <p data-lake-id="u0781742d" id="u0781742d"><span data-lake-id="u60775de7" id="u60775de7">​</span><br></p>
  <p data-lake-id="ua4fe83fc" id="ua4fe83fc"><span data-lake-id="uc7e2c75a" id="uc7e2c75a">​</span><br></p>
  <p data-lake-id="u9df4adec" id="u9df4adec"><strong><span data-lake-id="u2f80649b" id="u2f80649b">无锁</span></strong></p>
  <p data-lake-id="u46e94fce" id="u46e94fce"><strong><span data-lake-id="u1db185ef" id="u1db185ef">​</span></strong><br></p>
  <p data-lake-id="uf92268cb" id="uf92268cb"><span data-lake-id="u2608df78" id="u2608df78">当一个线程第一次访问一个对象的同步块时，JVM会在对象头中设置该线程的Thread ID，并将对象头的状态位设置为“偏向锁”。这个过程称为“偏向”，表示对象当前偏向于第一个访问它的线程。</span></p>
  <p data-lake-id="ufaaff42c" id="ufaaff42c"><br></p>
  <p data-lake-id="uf204f266" id="uf204f266"><span data-lake-id="ufaef0f13" id="ufaef0f13">​</span><br></p>
  <p data-lake-id="u08436b87" id="u08436b87"><strong><span data-lake-id="u3d090381" id="u3d090381">偏向锁（Biased Locking）</span></strong></p>
  <p data-lake-id="u2fee9062" id="u2fee9062"><span data-lake-id="uac180c0b" id="uac180c0b">偏向锁是指在只有一个线程访问对象的情况下，该线程不需要使用同步操作就可以访问对象。这种情况下，JVM 会在对象头中记录该线程的 ID 作为偏向锁的持有者，并将对象头中的 Mark Word 中的一部分作为偏向锁标识。在这种情况下，如果其他线程访问该对象，会先检查该对象的偏向锁标识，如果和自己的线程 ID 相同，则直接获取锁。如果不同，则该对象的锁状态就会升级到轻量级锁状态。</span></p>
  <p data-lake-id="u058f3c4d" id="u058f3c4d"><span data-lake-id="u38a18e79" id="u38a18e79">​</span><br></p>
  <p data-lake-id="ucc3ec3af" id="ucc3ec3af"><strong><span data-lake-id="udad1588e" id="udad1588e">轻量级锁（Lightweight Locking）</span></strong></p>
  <p data-lake-id="u7ad60713" id="u7ad60713"><br></p>
  <p data-lake-id="u1f7c666a" id="u1f7c666a"><span data-lake-id="ue824d745" id="ue824d745">在轻量级锁状态中，JVM 为对象头中的 Mark Word 预留了一部分空间，用于存储指向线程栈中锁记录的指针。</span></p>
  <p data-lake-id="ubffa7cb5" id="ubffa7cb5"><span data-lake-id="ua26c5f5f" id="ua26c5f5f">​</span><br></p>
  <p data-lake-id="u5ffcae1c" id="u5ffcae1c"><span data-lake-id="uc366b18b" id="uc366b18b">当一个线程访问该对象时，JVM 会将对象头中的 Mark Word 复制一份到线程栈中，并在对象头中存储线程栈中的指针。此时，如果另一个线程想要访问该对象，会发现该对象已经处于轻量级锁状态，并尝试使用 CAS 操作将对象头中的指针替换成自己的指针。</span></p>
  <p data-lake-id="uece9dcbb" id="uece9dcbb"><span data-lake-id="u5464d108" id="u5464d108">​</span><br></p>
  <p data-lake-id="u7036a5c2" id="u7036a5c2"><span data-lake-id="u10b636fe" id="u10b636fe">如果替换成功，则该线程获取锁成功；如果失败，则表示已经有其他线程获取了锁，则该锁状态就会升级到重量级锁状态。</span></p>
  <p data-lake-id="ued74c5ec" id="ued74c5ec"><span data-lake-id="u996ee125" id="u996ee125">​</span><br></p>
  <p data-lake-id="uc85bf17f" id="uc85bf17f"><strong><span data-lake-id="u47afd755" id="u47afd755">重量级锁（Heavyweight Locking）</span></strong></p>
  <p data-lake-id="u0b227469" id="u0b227469"><span data-lake-id="u4b75b000" id="u4b75b000">​</span><br></p>
  <p data-lake-id="u971219c2" id="u971219c2"><span data-lake-id="udbe9c4f2" id="udbe9c4f2">当锁状态升级到重量级锁状态时，JVM 会将该对象的锁变成一个重量级锁，并在对象头中记录指向等待队列的指针。</span></p>
  <p data-lake-id="uaa5479bb" id="uaa5479bb"><span data-lake-id="u57631aff" id="u57631aff">​</span><br></p>
  <p data-lake-id="u894aa271" id="u894aa271"><span data-lake-id="u5b26ddae" id="u5b26ddae">此时，如果一个线程想要获取该对象的锁，则需要先进入等待队列，等待该锁被释放。当锁被释放时，JVM 会从等待队列中选择一个线程唤醒，并将该线程的状态设置为“就绪”状态，然后等待该线程重新获取该对象的锁。</span></p>
  <p data-lake-id="u09de3929" id="u09de3929"><br></p>
  <p data-lake-id="ube704c98" id="ube704c98"><br></p>
  <p data-lake-id="uab3dd248" id="uab3dd248"><br></p>
 </body>
</html>